package com.startx.http.wordfilter;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 初始化敏感词库，将敏感词加入到HashMap中，构建DFA算法模型
 */
public class WordContext {

    /**
     * 敏感词读取器
     */
    private final IDictionaryReader reader;

    /**
     * 敏感词字典
     */
    private HashMap sensitiveWordMap;

    /**
     * 敏感词初始化Map的参数因子
     */
    private final float DEFAULT_INIT_MAP_SIZE_FACTORY = 0.75f;
    
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public WordContext(IDictionaryReader reader) {
        this.reader = reader;
        // 将敏感词库加入到HashMap中
        this.addSensitiveWordToHashMap(this.reader.readDicionaryData());
    }

    /**
     * 读取敏感词库，将敏感词放入HashSet中，构建一个DFA算法模型
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public void addSensitiveWordToHashMap(Set<String> keyWordSet) {
        try {
            lock.writeLock().lock();
            if (this.sensitiveWordMap == null) {
                int initSize = (int) (keyWordSet.size() * DEFAULT_INIT_MAP_SIZE_FACTORY);
                // 初始化敏感词容器，减少扩容操作，这里的Map大小，应该是所有敏感词的第一个字符去重后的个数
                this.sensitiveWordMap = new HashMap(initSize);
            }
            // 迭代keyWordSet
            for (String key : keyWordSet) {
                Map nowMap = this.sensitiveWordMap;
                for (int i = 0; i < key.length(); i++) {
                    char keyChar = key.charAt(i); // 转换成char型
                    Object wordMap = nowMap.get(keyChar); // 获取
                    if (wordMap != null) { // 如果存在该key，直接赋值
                        nowMap = (Map) wordMap;
                    } else { // 不存在则，则构建一个map，同时将isEnd设置为0，因为他不是最后一个
                        Map<String, String> newWorMap = new HashMap<>(2);
                        newWorMap.put("isEnd", "0"); // 不是最后一个
                        nowMap.put(keyChar, newWorMap);
                        nowMap = newWorMap;
                    }
                    if (i == key.length() - 1) {
                        nowMap.put("isEnd", "1"); // 最后一个
                    }
                }
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 删除敏感词库中的关键词
     *
     * @param keyWordSet
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public void removeSensitiveWordFromHashMap(Set<String> keyWordSet) {
        try {
            lock.writeLock().lock();

            KeySetIteration:
            for (String key : keyWordSet) {
                if (key == null || "".equals(key)) {
                    continue;
                }
                int length = key.length();
                Map[] allMap = new Map[length]; // 字符串以每个字符key为键值的map
                Map nowMap = this.sensitiveWordMap;
                for (int i = 0; i < length; i++) {
                    char keyChar = key.charAt(i);
                    Object wordMap = nowMap.get(keyChar);
                    if (wordMap == null) {
                        continue KeySetIteration; // 要求被删的词不在Map字典中
                    } else {
                        allMap[i] = nowMap; // 将每一层都存到allMap中
                        nowMap = (Map) wordMap;
                    }
                }
                // 处理allMap, 从末尾开始删除
                for (int j = length - 1; j >= 0; j--) {
                    Map item = allMap[j];
                    char keyChar = key.charAt(j); // 从最后一个字符串开始获取

                    Map child = (Map) item.get(keyChar);
                    if (child.keySet().size() == 1) {
                        // 如果他下面没有东西，只有isEnd，就干掉
                        item.remove(keyChar);
                    }
                    if ("1".equals(child.get("isEnd"))) {
                        // 它没有被干掉，说明他下面还有东西，但是这个词要干掉，所有标记0
                        child.put("isEnd", "0");
                    }
                }
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    public HashMap getSensitiveWordMap() {
        return this.sensitiveWordMap;
    }

    public ReentrantReadWriteLock getLock() {
        return lock;
    }
}
